package jaux.tank.generator.component;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.WindowConstants;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import jaux.tank.generator.domain.TankEntity;
import jaux.tank.generator.domain.TankEntity.DTO;
import jaux.tank.generator.domain.TankEntity.Paginate;
import jaux.tank.generator.domain.TankEntityField;
import jaux.tank.generator.domain.TankEntityField.FieldType;
import jaux.tank.generator.domain.TankEnum;
import jaux.tank.generator.jd.components.JdCheckBox;
import jaux.tank.generator.jd.components.JdCheckBoxGroup;
import jaux.tank.generator.jd.components.JdComboBox;
import jaux.tank.generator.jd.components.JdLabeledInput;
import jaux.tank.generator.jd.components.JdTextArea;
import jaux.tank.generator.jd.components.JdTextAreaCellEditor;
import jaux.tank.generator.jd.components.JdTextField;
import jaux.tank.generator.jd.components.JdUniqueTextCellEditor;
import jaux.tank.generator.jd.layout.JdGridLayout;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class EntityEditorPanel extends JPanel {
	private static final long serialVersionUID = 1L;

	private TankEntity entity;
	
	private  Supplier<List<TankEnum>> enumsSupplier;
	private List<JdLabeledInput<?,?> > inputs;

	@Getter
	@Setter
	private boolean saved;

	private int fieldStartIndex=1;
	private JTable fieldsTable;

	public EntityEditorPanel(TankEntity entity, @NonNull Supplier<List<TankEnum>> enumsSupplier) {
		super();
		this.entity = entity;
		this.enumsSupplier = enumsSupplier;
		initializeComponents();
	}

	private void initializeComponents() {
		setLayout(new BorderLayout(5, 5));
		
		List<JdLabeledInput<?,?> > inputs = new ArrayList<>();
		// basic panel
		JPanel basicPanel = new JPanel();
		basicPanel.setLayout(new JdGridLayout(0,2));
		
		add(basicPanel, BorderLayout.NORTH);
		// use jd components to edit entity
		// name field
		JdTextField nameTextField = new JdTextField("Name:", entity.getName());
		nameTextField.setEditable(false);
		basicPanel.add(nameTextField);
		inputs.add(nameTextField);
		// dto field
		JdComboBox<DTO> dtoComboBox = new JdComboBox<>("DTO:", entity.getDto(), DTO.values());
		dtoComboBox.onChange(value -> entity.setDto(value));
		basicPanel.add(dtoComboBox);
		inputs.add(dtoComboBox);
		// table name field
		JdTextField tableNameTextField = new JdTextField("Table Name:", entity.getTableName());
		tableNameTextField.onChange(value -> entity.setTableName(value));
		basicPanel.add(tableNameTextField);
		inputs.add(tableNameTextField);
		// comment field
		JdTextField commentTextField = new JdTextField("Comment:", entity.getComment());
		commentTextField.onChange(value -> entity.setComment(value));
		basicPanel.add(commentTextField);
		inputs.add(commentTextField);
		// javadocs field
		List<String> javaDocs = entity.getJavaDocs();
		JdTextArea javadocTextField = new JdTextArea("Javadoc:", javaDocs.toArray(new String[0]));
		javadocTextField.setRows(Math.max(3, javaDocs.size()));
		javadocTextField.setInputComponentSize(0, 60);
		javadocTextField.onChange(value -> entity.setJavaDocs(Arrays.asList(value)));
		basicPanel.add(javadocTextField);
		inputs.add(javadocTextField);
		// annotations field
		List<String> annotations = entity.getAnnotations();
		JdTextArea annotationTextField = new JdTextArea("Annotation:", annotations.toArray(new String[0]));
		javadocTextField.setRows(Math.max(3, annotations.size()));
		annotationTextField.setInputComponentSize(0, 60);
		annotationTextField.onChange(value -> entity.setAnnotations(Arrays.asList(value)));
		basicPanel.add(annotationTextField);
		inputs.add(annotationTextField);
		// service type field
		JdComboBox<TankEntity.ServiceType> serviceTypeComboBox = new JdComboBox<>("Service Type:",
				entity.getServiceType(), TankEntity.ServiceType.values());
		serviceTypeComboBox.onChange(value -> entity.setServiceType(value));
		basicPanel.add(serviceTypeComboBox);
		inputs.add(serviceTypeComboBox);
		// paginates field
		JdComboBox<Paginate> paginateComboBox = new JdComboBox<>("Paginate:", entity.getPaginate(),
				Paginate.values());
		paginateComboBox.onChange(value -> entity.setPaginate(value) );
		basicPanel.add(paginateComboBox);
		inputs.add(paginateComboBox);
		// filtered field
		JdCheckBox filteredCheckBox = new JdCheckBox("Filtered:", entity.isFiltered());
		filteredCheckBox.onChange(value -> entity.setFiltered(value));
		basicPanel.add(filteredCheckBox);
		inputs.add(filteredCheckBox);
		// readonly field
		JdCheckBox readOnlyCheckBox = new JdCheckBox("Readonly:", entity.isReadOnly());
		readOnlyCheckBox.onChange(value -> entity.setReadOnly(value));
		basicPanel.add(readOnlyCheckBox);
		inputs.add(readOnlyCheckBox);
		// skip client field
		JdCheckBox skipClientCheckBox = new JdCheckBox("Skip Client:", entity.isSkipClient());
		skipClientCheckBox.onChange(value -> entity.setSkipClient(value));
		basicPanel.add(skipClientCheckBox);
		inputs.add(skipClientCheckBox);
		// skip server field
		JdCheckBox skipServerCheckBox = new JdCheckBox("Skip Server:", entity.isSkipServer());
		skipServerCheckBox.onChange(value -> entity.setSkipServer(value));
		basicPanel.add(skipServerCheckBox);
		inputs.add(skipServerCheckBox);

		// audit enabled field
		JdCheckBox auditEnabledCheckBox = new JdCheckBox("Audit:", entity.isAuditEnabled());
		auditEnabledCheckBox.onChange(value -> entity.setAuditEnabled(value));
		basicPanel.add(auditEnabledCheckBox);
		inputs.add(auditEnabledCheckBox);
		
		// soft delete enabled field
		JdCheckBox softDeleteEnabledCheckBox = new JdCheckBox("Soft Delete:", entity.isSoftDeleteEnabled());
		softDeleteEnabledCheckBox.onChange(value -> entity.setSoftDeleteEnabled(value));
		basicPanel.add(softDeleteEnabledCheckBox);
		inputs.add(softDeleteEnabledCheckBox);
		
		// fields panel
		JPanel fieldsPanel = new JPanel();
		add(fieldsPanel, BorderLayout.CENTER);
		fieldsPanel.setLayout(new BorderLayout(5, 1));
		fieldsPanel.setBorder(BorderFactory
				.createTitledBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, Color.LIGHT_GRAY), "Fields:"));
		// 将表格放入滚动面板中
		genFieldsTable();
		JScrollPane scrollPane = new JScrollPane(fieldsTable);
		fieldsPanel.add(scrollPane, BorderLayout.CENTER);
		JPanel fieldsOperationPanel = new JPanel();
		fieldsOperationPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
		fieldsPanel.add(fieldsOperationPanel, BorderLayout.SOUTH);
		JButton addButton = new JButton();
		addButton.setText("Add Field");
		addButton.addActionListener(e -> {
			((DefaultTableModel)fieldsTable.getModel()).addRow(new TankEntityField(genNewFieldName(), FieldType.STRING).toRowData());
		});
		fieldsOperationPanel.add(addButton);
		JButton deleteButton = new JButton();
		deleteButton.setText("Delete Field");
		deleteButton.addActionListener(e -> {
			int delRow = fieldsTable.getSelectedRow();
			if (delRow == -1) {
				return;
			}
			((DefaultTableModel)fieldsTable.getModel()).removeRow(delRow);
			
		});
		fieldsOperationPanel.add(deleteButton);
		// 遍历所有的JdLabeledInput组件,为每个组件注册change事件
		for (JdLabeledInput<?,?> fieldInput : inputs) {
			// 当JdLabeledInput组件内容变化时，更新保存按钮的状态
			fieldInput.onChange(e -> this.saved= false);
			fieldInput.setLabelSize(80, 0);
			fieldInput.setInputComponentSize(250, 0);
		}
		// 当fieldsTable内容变化时，更新保存按钮的状态
		fieldsTable.getModel().addTableModelListener(e -> {
			this.saved = false;
			// get entity fields from table
			List<TankEntityField> fields = new ArrayList<>();
			for (int i = 0; i < fieldsTable.getRowCount(); i++) {
				final int row = i;
				Object[] rowData = IntStream.range(0, TankEntityField.COLUMNS.length).mapToObj(j -> fieldsTable.getValueAt(row, j)).toArray();
				TankEntityField field = TankEntityField.fromRowData(rowData);
				fields.add(field);
			}
			entity.setFields(fields);
		});

	}

	private JTable genFieldsTable() {
		// table

		// 创建表头
		Object[] columns = TankEntityField.COLUMNS;
		// 创建表格数据
		List<Object[]> data = new ArrayList<>();
		for (TankEntityField field : entity.getFields()) {
			data.add(field.toRowData());
		}

		// 创建默认的表格模型
		DefaultTableModel model = new DefaultTableModel(data.toArray(new Object[0][0]), columns) {
			@Override
			public Class<?> getColumnClass(int columnIndex) {
				return String.class; // 设置列的类为String类型
			}
			@Override
			public boolean isCellEditable(int row, int column) {
				Object fieldName = this.getValueAt(row, 0);
				Object fieldType = this.getValueAt(row, 1);
				Object cellValue = this.getValueAt(row, column);
				Object columnName = this.getColumnName(column);
				int originalColumn = Arrays.asList(TankEntityField.COLUMNS).indexOf(columnName);
				if(TankEntity.ID_FIELD_NAME.equals(fieldName)) {
					return false;
				}
				if( (originalColumn == 7 || originalColumn ==8) && !Arrays.asList(
						FieldType.INTEGER, FieldType.LONG, FieldType.DOUBLE, FieldType.FLOAT, FieldType.BIG_DECIMAL).contains(fieldType)) {
					return false;
				}
				if( (originalColumn == 9 || originalColumn ==10) && !Arrays.asList(
						FieldType.STRING, FieldType.BLOB, FieldType.ANY_BLOB, FieldType.IMAGE_BLOB, FieldType.TEXT_BLOB).contains(fieldType)) {
					return false;
				}
				if(originalColumn == 11 &&!FieldType.STRING.equals(fieldType)) {
					return false;
				}
				if(originalColumn == 2 &&!FieldType.ENUM.equals(fieldType)) {
					return false;
				}
				
				return true;
			}
		};
		// 创建表格并设置模型
		fieldsTable = new JTable(model);
		fieldsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); // 禁用自动调整列宽
		
		// 禁止列顺序变化
		fieldsTable.getTableHeader().setReorderingAllowed(false);
		fieldsTable.setRowHeight(20); // 设置行高
		// check field name exists
		DefaultCellEditor fieldNameEditor = new  JdUniqueTextCellEditor(()->entity.getFields().stream().map(TankEntityField::getName).collect(Collectors.toList())
				, "Field with same name already exists");
		fieldsTable.getColumnModel().getColumn(0).setCellEditor(fieldNameEditor);
		
		// 创建 JComboBox 作为type列单元格编辑器
		TankEntityField.FieldType[] fieldTypes = TankEntityField.FieldType.values();
		JComboBox<TankEntityField.FieldType> comboBox = new JComboBox<>(fieldTypes);
		TableCellEditor comboBoxEditor = new DefaultCellEditor(comboBox);
		fieldsTable.getColumnModel().getColumn(1).setCellEditor(comboBoxEditor);

		// use jcombobox for enum column
		JComboBox<String> enumComboBox = new JComboBox<>();
		enumComboBox.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseEntered(MouseEvent e) {
				List<String> enumNames = new ArrayList<>();
				if(enumsSupplier  != null && enumsSupplier.get() != null) {
					enumNames = enumsSupplier.get().stream().map(TankEnum::getName).collect(Collectors.toList());
				}
				enumComboBox.setModel(new DefaultComboBoxModel<>(enumNames.toArray(new String[0])));
				super.mouseEntered(e);
			}
		});
		TableCellEditor enumComboBoxEditor = new DefaultCellEditor(enumComboBox);
		fieldsTable.getColumnModel().getColumn(2).setCellEditor(enumComboBoxEditor);
		
		// use JComboBox for required column
		JComboBox<Boolean> requiredComboBox = new JComboBox<>(new Boolean[] { true, false });
		TableCellEditor requiredComboBoxEditor = new DefaultCellEditor(requiredComboBox);
		fieldsTable.getColumnModel().getColumn(5).setCellEditor(requiredComboBoxEditor);

		// use JComboBox for unique column
		JComboBox<Boolean> uniqueComboBox = new JComboBox<>(new Boolean[] { true, false });
		TableCellEditor uniqueComboBoxEditor = new DefaultCellEditor(uniqueComboBox);
		fieldsTable.getColumnModel().getColumn(6).setCellEditor(uniqueComboBoxEditor);

		// use jtextarea for javaDocs column
		fieldsTable.getColumnModel().getColumn(12).setCellEditor(new JdTextAreaCellEditor());
		// use jtextarea for annotations column
		fieldsTable.getColumnModel().getColumn(13).setCellEditor(new JdTextAreaCellEditor());
		return fieldsTable;
	}

	private String genNewFieldName() {
		for(int i=fieldStartIndex;i<fieldStartIndex+10;i++) {
			String newFieldName = "newField"+i;
			boolean exists = false;
			for(TankEntityField field : entity.getFields()) {
				if(field.getName().equals(newFieldName)) {
					exists = true;
					break;
				}
			}
			if(!exists) {
				fieldStartIndex = i+1;
				return newFieldName;
			}
		}
		return "newField"+System.currentTimeMillis();
	}
	public TankEntity getEntity() {
		// stop   fields editing
		TableCellEditor activeEditor = fieldsTable.getCellEditor();
		if(activeEditor != null) {
			activeEditor.stopCellEditing();
		}
		return this.entity;
	}
	

	public static void main(String[] args) {
		// test
		JFrame frame = new JFrame();
		frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		frame.setLayout(new BorderLayout(0, 0));
		TankEntity entity = new TankEntity("abc");
		TankEntityField nameField = new TankEntityField();
		nameField.setName("name");
		nameField.setType(TankEntityField.FieldType.STRING);
		nameField.setComment("name");
		nameField.setJavaDocs(Arrays.asList("name"));
		nameField.setAnnotations(Arrays.asList("name"));
		nameField.setValidation(new TankEntityField.FieldValidation());
		nameField.setDefaultValue("aaa");
		entity.setFields(Arrays.asList(nameField));
		EntityEditorPanel entityEditorPanel = new EntityEditorPanel(entity,()->Arrays.asList(new TankEnum("Sex")));
		frame.getContentPane().add(entityEditorPanel, BorderLayout.CENTER);
		// get value button
		JButton getValueButton = new JButton();
		getValueButton.setText("get value");
		getValueButton.addActionListener(e -> {
			String entityJSON =null;
			try {
				entityJSON = new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(entityEditorPanel.getEntity());
			} catch (JsonProcessingException e1) {
				e1.printStackTrace();
			}
			JDialog dialog = new JDialog();
			dialog.getContentPane().add(new JScrollPane(new JTextArea(entityJSON)));
			dialog.pack();
			dialog.setVisible(true);
		});
		frame.getContentPane().add(getValueButton, BorderLayout.SOUTH);
		frame.pack();
		frame.setVisible(true);
	}
}
